package com.wpf.proxy;

import com.wpf.beans.MethodInfo;
import com.wpf.beans.ServerInfo;
import com.wpf.flux2client.server.ApiServer;
import com.wpf.handlers.FluxClientRestHandler;
import com.wpf.interfaces.ProxyCreator;
import com.wpf.interfaces.RestHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpMethod;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.LinkedHashMap;
import java.util.Map;

@Slf4j
public class JDKProxyCreator implements ProxyCreator {

    @Override
    public Object createProxy(Class<?> clazz) {

        //服务器信息
        ServerInfo serverInfo = getServerInfo(clazz);

        log.info("serverInfo: " + serverInfo);

        //  使用了比较精巧的模式设置值
        RestHandler handler = new FluxClientRestHandler();
        handler.init(serverInfo);

        // 使用JDK代理的方式创建对象
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                new Class[]{clazz}, new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                        MethodInfo methodInfo = getMethodInfo(method,args);

                        log.info("methodInfo: "+methodInfo);

                        return handler.invokeRest(methodInfo);
                    }

                    private MethodInfo getMethodInfo(Method method, Object[] args) {
                        MethodInfo methodInfo = new MethodInfo();

                        // 获取到URL和请求方法
                        Annotation[] annotations = method.getDeclaredAnnotations();
                        for (Annotation annotation:annotations){

                            //GET
                            if(annotation instanceof GetMapping){
                                GetMapping a = (GetMapping)annotation;
                                methodInfo.setUrl(a.value()[0]);
                                methodInfo.setMethod(HttpMethod.GET);
                            }
                            //POST
                            else if(annotation instanceof PostMapping){
                                PostMapping a = (PostMapping)annotation;
                                methodInfo.setUrl(a.value()[0]);
                                methodInfo.setMethod(HttpMethod.POST);
                            }
                            //DELETE
                            else if(annotation instanceof DeleteMapping){
                                DeleteMapping a = (DeleteMapping)annotation;
                                methodInfo.setUrl(a.value()[0]);
                                methodInfo.setMethod(HttpMethod.DELETE  );
                            }

                        }

                        //获取请求参数已经参数的注解(得到请求的param和body)
                        Parameter[] parameters = method.getParameters();
                        Map<String,Object> map = new LinkedHashMap<>();
                        methodInfo.setParams(map);
                        for (int i=0;i<parameters.length;i++){
                            PathVariable pathVariable = parameters[i].getAnnotation(PathVariable.class);
                            if(pathVariable != null){
                                map.put(pathVariable.value(),args[i]);
                            }

                            //  判断是否带了 RequestBody
                            //  通过反射机制 可以获取到所有的参数信息
                            RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
                            if(requestBody != null){
                                methodInfo.setBody((Mono<?>)args[i]);
                                // 得到RequestBody的信息
                                methodInfo.setBodyType(
                                        (Class<?>)((ParameterizedType)parameters[i].getParameterizedType()).getActualTypeArguments()[0]
                                );
                            }
                        }

                        // 提取返回对象信息
                        boolean isFlux = method.getReturnType().isAssignableFrom(Flux.class);
                        methodInfo.setReturnFlux(isFlux);

                        // 提取到返回对象的真实类型
                        Class<?> type = (Class<?>) ((ParameterizedType)method.getGenericReturnType()).getActualTypeArguments()[0];
                        methodInfo.setReturnElementType(type);
                        return methodInfo;
                    }
                });
    }


    /**
     * 通过反射得到服务器端的信息  url
     * @param clazz
     * @return
     */
    private ServerInfo getServerInfo(Class<?> clazz) {
        ServerInfo serverInfo = new ServerInfo();
        ApiServer annotation = clazz.getAnnotation(ApiServer.class);
        serverInfo.setUrl(annotation.value());
        return serverInfo;
    }
}
